package com.shop.shopserver.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.shop.shoppojo.entity.Category;
import com.shop.shoppojo.vo.CategoryVO;
import com.shop.shopserver.mapper.CategoryMapper;
import com.shop.shopserver.service.CategoryService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
@CacheConfig(cacheNames = "categoryTreeCaffeineCache")
public class CategoryServiceImpl implements CategoryService {

    @Autowired
    private CategoryMapper categoryMapper;
    @Autowired
    private final RedisTemplate<String, byte[]> redisTemplate;
    @Autowired
    private final ObjectMapper objectMapper;

    /**
     * 商品全部分类
     * @param categories
     * @param parentId
     * @return
     */


   //构建分类树方法
    private List<CategoryVO> buildTree(List<Category> categories, Long parentId) {
        List<CategoryVO> categoryVOList = new ArrayList<>();

        return categories.stream()
                .filter(category -> parentId == null || (category.getParentId() != null && category.getParentId().equals(parentId)))
                .map(category -> {
                    System.out.println("Processing category: " + category.getId());
                    CategoryVO categoryVO = new CategoryVO();
                    BeanUtils.copyProperties(category, categoryVO);
                    System.out.println(category +""+ categoryVO);
                    categoryVO.setChildren(buildTree(categories, category.getId()));
                    categoryVOList.add(categoryVO);
                    return categoryVO;
                })
                .collect(Collectors.toList());

    }


   //从数据库中查询所有商品分类，并构建出商品分类树结构，并返回该树结构
    public List<CategoryVO> getCategoryTree() {
        List<Category> categories = categoryMapper.findAll();
        // 判断如果数据库中没有数据，直接返回空列表作为仿穿透标识
        if (categories.isEmpty()) {
            // 将标识设置到redis中
            redisTemplate.opsForValue().set("categoryTree", "".getBytes());
            return new ArrayList<>();
        }
        List<CategoryVO> categoryTree = buildTree(categories, null);
        return categoryTree;
    }

   //从redis中查询商品分类树结构，并返回该树结构
    public List<CategoryVO> getCategoryTreeFromRedis() {
        log.info("本地缓存没有，到 redis 中找");
        try {
            byte[] value = redisTemplate.opsForValue().get("categoryTree");
            if (value!= null) {
                log.info("redis 中有");
                List<CategoryVO> categoryTree = objectMapper.readValue(value, objectMapper.getTypeFactory().constructCollectionType(List.class, CategoryVO.class));
                //使用objectMapper将字节数组反序列化为List<CategoryVO>类型的对象categoryTree，然后返回这个categoryTree
                return categoryTree;
            } else {
                log.info("redis 中没有");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    //获取商品分类主方法
    @Cacheable
    public List<CategoryVO> getData(){
        log.info("缓存中没有，到redis中找");
        List <CategoryVO> categoryTree = getCategoryTreeFromRedis();
        if (categoryTree == null) {
            System.out.println("redis中没有，到数据库");
            // redis 中没有则从数据库获取
            categoryTree = getCategoryTree();
            // 如果从数据库获取到数据，更新Redis
            updateRedis(categoryTree);
            return categoryTree;
        }
        //redis有 返回redis的数据
        return categoryTree;
    }


    //更新redis缓存
    public void updateRedis(List<CategoryVO> categoryTree) {
        try {
            byte[] value = objectMapper.writeValueAsBytes(categoryTree);
            //将分类树序列化为字节数组，并保存到Redis中
            redisTemplate.opsForValue().set("categoryTree", value);
            log.info("更新了Redis");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //定时任务，每隔5分钟刷新商品分类树结构
    @Scheduled(fixedRate = 5 * 30 * 1000) // 每5分钟执行一次
    @CacheEvict(allEntries = true)
    public void refreshCategoryTree() {
        List<CategoryVO> categoryTree = getCategoryTree();
        try {
            //更新redis
            updateRedis(categoryTree);
            log.info("job 更新了");
        } catch (Exception e) {
            // 处理异常，例如记录日志
            e.printStackTrace();
        }
    }
}
